# -*- coding: utf-8 -*-
import ast
import hashlib
import json
from collections import OrderedDict

import requests
from django.conf import settings
from django.template import Context
from django.template.loader import get_template

from async import async_job
from common.agentpay.base import AbstractHandler
from common.agentpay.db import get_agent_pay_order
from common.cache import redis_cache
from common.channel import admin_db as channel_admin_db
from common.order import db as order_db
from common.order.model import PAY_STATUS
from common.utils import track_logging
from common.utils.exceptions import ParamError
from common.utils.qr import make_code
from common.utils.types import Enum
from common.utils.ip_address import check_valid_ip_address

_LOGGER = track_logging.getLogger(__name__)

NOTIFY_PAY_STATUS = Enum({
    "WAIT": (0, u"等待"),
    "SUCCESS": (1, u"成功"),
    "FAIL": (2, u"失败"),
})

APP_CONF = {
    '1023': {  # zs 支付宝：3.3%（1---3000），微信3.3%（1---2000）都是d1结算。qq1.8%(1---1000),快捷（10---5000）都是d0结算。
        'API_KEY': 'C6F4B6C4531F41D1B4F69DB28EF75171',
        'partner_id': '10000000144',
        'gateway': 'http://pay02.bjxinxq.com:9003/pay/createOrder',
        'query_gateway': 'http://pay02.bjxinxq.com:9003/pay/order/status?partnerId={}&appId={}&outTradeNo={}&sign={}',
        'draw_gateway': 'http://admin02.bjxinxq.com:9013/manage/pay/doTransfer',
        'draw_query_gateway': 'http://admin02.bjxinxq.com:9013/manage/pay/queryOrder',
    },
    '1027': {  # dwc 支付宝3.3%（1---2000），微信3.3%（1---1000）快捷1.6%（1---5000），D1结算
        'API_KEY': '067752683D554E83B264AEE09F5EADA6',
        'partner_id': '10000000148',
        'gateway': 'http://pay02.bjxinxq.com:9003/pay/createOrder',
        'query_gateway': 'http://pay02.bjxinxq.com:9003/pay/order/status?partnerId={}&appId={}&outTradeNo={}&sign={}',
    },
    '1008': {  # dbl 支付宝3.3%（1---2000），微信3.3%（1---1000）快捷1.6%（1---5000），D1结算
        'API_KEY': '58E0039E590F48A99CFF9AF6CA7AFC81',
        'partner_id': '10000000133',
        'gateway': 'http://pay02.bjxinxq.com:9003/pay/createOrder',
        'query_gateway': 'http://pay02.bjxinxq.com:9003/pay/order/status?partnerId={}&appId={}&outTradeNo={}&sign={}',
    },
    '1068': {  # loki 支付宝3.3%（200---10000），快捷1.6%（10----5000），D0结算
        'API_KEY': 'D5381374141B4C72ACC62A47EA97D5AE',
        'partner_id': '10000000175',
        'gateway': 'http://pay02.bjxinxq.com:9003/pay/createOrder',
        'query_gateway': 'http://pay02.bjxinxq.com:9003/pay/order/status?partnerId={}&appId={}&outTradeNo={}&sign={}',
        'draw_gateway': 'http://admin02.bjxinxq.com:9013/manage/pay/doTransfer',
        'draw_query_gateway': 'http://admin02.bjxinxq.com:9013/manage/pay/queryOrder',
    },
    '1067': {  # tt 支付宝3.3%（200---10000），快捷1.6%（10----5000），D0结算
        'API_KEY': 'BD5FF5946E9F4A46A17CDEE70885DCB2',
        'partner_id': '10000000176',
        'gateway': 'http://pay02.bjxinxq.com:9003/pay/createOrder',
        'query_gateway': 'http://pay02.bjxinxq.com:9003/pay/order/status?partnerId={}&appId={}&outTradeNo={}&sign={}',
        'draw_gateway': 'http://admin02.bjxinxq.com:9013/manage/pay/doTransfer',
        'draw_query_gateway': 'http://admin02.bjxinxq.com:9013/manage/pay/queryOrder',
        'balance_query_gateway': 'http://admin02.bjxinxq.com:9013/manage/pay/queryBalance',
    },
    '1069': {  # dwc 支付宝2.7%（200---10000）
        'API_KEY': '3904052402D74C24B2F8396CEB9B5C9E',
        'DRAW_KEY': 'EBF578FBDC9C48E9B51E9D9402952B14',
        'partner_id': '10000000178',
        'gateway': 'http://pay02.bjxinxq.com:9003/pay/createOrder',
        'query_gateway': 'http://pay02.bjxinxq.com:9003/pay/order/status?partnerId={}&appId={}&outTradeNo={}&sign={}',
        'draw_gateway': 'http://admin02.bjxinxq.com:9013/manage/pay/doTransfer',
        'draw_query_gateway': 'http://admin02.bjxinxq.com:9013/manage/pay/queryOrder',
        'balance_query_gateway': 'http://admin02.bjxinxq.com:9013/manage/pay/queryBalance',
    },
    '1071': {  # dwc 快捷1.6，单笔（10---5000）。微信3.6%单笔（10 20 30	50 100 200固定金额的充值）
        'API_KEY': '547F58C1E2C343F1A0C7406FB7588429',
        'partner_id': '10000000179',
        'gateway': 'http://pay02.bjxinxq.com:9003/pay/createOrder',
        'query_gateway': 'http://pay02.bjxinxq.com:9003/pay/order/status?partnerId={}&appId={}&outTradeNo={}&sign={}',
    },
    '1092': {
        # witch 快捷1.6，单笔（10---5000）。
        # 支付宝定额 2.7% 50,100,200,300,400,500,600,700,800,900,1000,2000,3000,5000
        'API_KEY': 'C89088C906CC46DD8CDB4CC8B73B6B4E',
        'partner_id': '10000000201',
        'gateway': 'http://pay02.bjxinxq.com:9003/pay/createOrder',
        'query_gateway': 'http://pay02.bjxinxq.com:9003/pay/order/status?partnerId={}&appId={}&outTradeNo={}&sign={}',
    },
    '1099': {
        # dwc 支付宝3.6，10 20 30 50 100
        'API_KEY': 'E71B2D122F064AF19D8DE8D3C42A495F',
        'partner_id': '10000000205',
        'gateway': 'http://pay02.bjxinxq.com:9003/pay/createOrder',
        'query_gateway': 'http://pay02.bjxinxq.com:9003/pay/order/status?partnerId={}&appId={}&outTradeNo={}&sign={}',
        'draw_gateway': 'http://admin02.bjxinxq.com:9013/manage/pay/doTransfer',
        'draw_query_gateway': 'http://admin02.bjxinxq.com:9013/manage/pay/queryOrder',
        'balance_query_gateway': 'http://admin02.bjxinxq.com:9013/manage/pay/queryBalance',
    },
}


def _get_api_key(mch_id):
    return APP_CONF[mch_id]['API_KEY']


def _get_draw_key(mch_id):
    return APP_CONF[mch_id]['DRAW_KEY']


def _get_partner_id(mch_id):
    return APP_CONF[mch_id]['partner_id']


def _get_gateway(mch_id):
    return APP_CONF[mch_id]['gateway']


def _get_query_gateway(mch_id):
    return APP_CONF[mch_id]['query_gateway']


def _get_draw_gateway(mch_id):
    return APP_CONF[mch_id]['draw_gateway']


def _get_draw_query_gateway(mch_id):
    return APP_CONF[mch_id]['draw_query_gateway']


def _get_balance_gateway(mch_id):
    return APP_CONF[mch_id]['balance_query_gateway']


def generate_sign(parameter, key):
    s = ''
    for k in sorted(parameter.keys()):
        if parameter[k] != '' and parameter[k] is not None:
            s += '%s=%s&' % (k, parameter[k])
    s += 'key=%s' % key
    m = hashlib.md5()
    m.update(s.encode('utf8'))
    sign = m.hexdigest().upper()
    return sign


def verify_notify_sign(params, key):
    sign = params['sign']
    params.pop('sign')
    calculated_sign = generate_sign(params, key)
    if sign != calculated_sign:
        _LOGGER.info("xunlianpay sign: %s, calculated sign: %s", sign, calculated_sign)
        raise ParamError('sign not pass, data: %s' % params)


def _get_pay_type(service):
    if service == 'qq':
        return 6
    elif service == 'quick':
        return 8
    elif service == 'alipay':
        return 2
    elif service == 'weixin':
        return 1
    elif service == 'wechat':
        return 4  # 微信H5
    else:
        return 6


def _get_bank_type(bank_type):
    if bank_type == 'ICBC':
        return 'ICBC'
    elif bank_type == 'CCB':
        return 'CCB'
    elif bank_type == 'PNB':
        return 'PABC'
    elif bank_type == 'BOC':
        return 'BOC'
    elif bank_type == 'ABC':
        return 'ABC'
    elif bank_type == 'BCM':
        return 'BOCOM'
    elif bank_type == 'CMB':
        return 'CMB'
    elif bank_type == 'CNCB':
        return 'CITIC'
    elif bank_type == 'CEB':
        return 'CEB'
    elif bank_type == 'HXB':
        return 'HXB'
    elif bank_type == 'SPDB':
        return 'SPDB'
    elif bank_type == 'CIB':
        return 'CIB'
    elif bank_type == 'CMBC':
        return 'CMBC'
    elif bank_type == 'CGB':
        return 'CGB'
    elif bank_type == 'PSBC':
        return 'PSBC'
    else:
        return 'THIRD_NOT_SUPPORT'


def _get_bank_name(bank_type):
    if bank_type == 'ICBC':
        return '工商银行'
    elif bank_type == 'CCB':
        return '建设银行'
    elif bank_type == 'PNB':
        return '平安银行'
    elif bank_type == 'BOC':
        return '中国银行'
    elif bank_type == 'ABC':
        return '农业银行'
    elif bank_type == 'BCM':
        return '交通银行'
    elif bank_type == 'CMB':
        return '招商银行'
    elif bank_type == 'CNCB':
        return '中信银行'
    elif bank_type == 'CEB':
        return '光大银行'
    elif bank_type == 'HXB':
        return '华夏银行'
    elif bank_type == 'SPDB':
        return '浦发银行'
    elif bank_type == 'CIB':
        return '兴业银行'
    elif bank_type == 'CMBC':
        return '民生银行'
    elif bank_type == 'CGB':
        return '广发银行'
    elif bank_type == 'PSBC':
        return '邮政银行'
    else:
        return 'THIRD_NOT_SUPPORT'


def _get_device_ip(info):
    try:
        extra = json.loads(info['extra'])
    except:
        extra = {}
    user_info = extra.get('user_info', {})
    return user_info.get('device_ip') or '127.0.0.1'


def create_charge(pay, pay_amount, info):
    ''' 创建订单 '''
    service = info['service']
    app_id = info['app_id']
    api_key = _get_api_key(app_id)
    p_dict = OrderedDict((
        ('appId', app_id),
        ('bankCardNo', str(pay.user_id)),
        ('clientIp', _get_device_ip(info)),
        ('money', str(int(pay_amount * 100))),
        ('outTradeNo', str(pay.id)),
        ('partnerId', _get_partner_id(app_id)),
        ('returnUrl', '{}/pay/api/{}/xunlianpay/{}'.format(settings.NOTIFY_PREFIX, settings.NOTIFY_PATH, app_id)),
        ('subject', 'recharge'),
        ('wapType', _get_pay_type(service)),
    ))
    p_dict['sign'] = generate_sign(p_dict, api_key)
    _LOGGER.info('xunlianpay create data: %s', json.dumps(p_dict))
    response = requests.post(_get_gateway(app_id), json=p_dict, timeout=5, allow_redirects=False)
    _LOGGER.info("xunlianpay create rsp data: %s %s", response.status_code, response.text)
    j = response.json()['data']
    # 1 URL 2 HTML
    if str(j['responseType']) == '2':
        cache_id = redis_cache.save_html(pay.id, j['value'])
        url = settings.PAY_CACHE_URL + cache_id
    else:
        if service == 'wechat':
            template_data = {'base64_img': make_code(j['value']), 'amount': pay_amount}
            t = get_template('qr_wechat_xunlian.html')
            html = t.render(Context(template_data))
            cache_id = redis_cache.save_html(pay.id, html)
            _LOGGER.info("xunlianpay create_url: %s, pay.id: %s", settings.PAY_CACHE_URL + cache_id, pay.id)
            url = settings.PAY_CACHE_URL + cache_id
        else:
            url = j['value']
    return {'charge_info': url}


# 0
def check_notify_sign(request, app_id):
    data = json.loads(request.body)
    api_key = _get_api_key(app_id)
    _LOGGER.info("xunlianpay notify data: %s", data)
    verify_notify_sign(data, api_key)
    pay_id = data['outTradeNo']
    check_valid_ip_address(str(request.META['REMOTE_ADDR']), pay_id)
    if not pay_id:
        _LOGGER.error("fatal error, out_trade_no not exists, data: %s" % data)
        raise ParamError('xunlianpay event does not contain pay ID')

    pay = order_db.get_pay(int(pay_id))
    if not pay:
        raise ParamError('pay_id: %s invalid' % pay_id)
    if pay.status != PAY_STATUS.READY:
        raise ParamError('pay %s has been processed' % pay_id)

    mch_id = pay.mch_id
    trade_status = str(data['code'])
    trade_no = data['invoiceNo']
    total_fee = float(data['money']) / 100.0

    extend = {
        'trade_status': trade_status,
        'trade_no': trade_no,
        'total_fee': total_fee
    }

    from common.channel.pay import check_channel_order
    check_channel_order(pay_id, total_fee, app_id)
    # 0 交易成功
    # 1 交易失败(保留值，交易失败暂.
    if trade_status == '0':
        _LOGGER.info('xunlianpay check order success, mch_id:%s pay_id:%s' % (mch_id, pay_id))
        order_db.add_pay_success(mch_id, pay_id, total_fee, trade_no, extend)
        async_job.notify_mch(pay_id)


def query_charge(pay_order, app_id):
    ''' 查询订单 '''
    api_key = _get_api_key(app_id)
    p_dict = OrderedDict((
        ('partnerId', _get_partner_id(app_id)),
        ('appId', app_id),
        ('outTradeNo', pay_order.id),
    ))
    p_dict['sign'] = generate_sign(p_dict, api_key)
    url = _get_query_gateway(app_id).format(p_dict['partnerId'], p_dict['appId'], p_dict['outTradeNo'], p_dict['sign'])
    _LOGGER.info('xunlianpay query url, %s', url)
    response = requests.get(url)
    _LOGGER.info('xunlianpay query rsp: %s', response.text)
    if response.status_code == 200:
        data = json.loads(response.text)
        mch_id = pay_order.mch_id
        trade_status = str(data['code'])
        total_fee = float(pay_order.total_fee)
        trade_no = ''

        extend = {
            'trade_status': trade_status,
            'total_fee': total_fee
        }

        if trade_status == '1' and total_fee > 0:
            _LOGGER.info('xunlianpay query order success, mch_id:%s pay_id:%s' % (mch_id, pay_order.id))
            order_db.add_pay_success(pay_order.mch_id, pay_order.id, total_fee, trade_no, extend)
            async_job.notify_mch(pay_order.id)


class XunlianPayHandler(AbstractHandler):
    def submit_order(self, order_no):
        """
        创建代付请求
        :return:
        """
        agent_pay = get_agent_pay_order(order_no)
        chn = channel_admin_db.get_channel(int(agent_pay.channel_id))

        chn_info = json.loads(chn.info)
        app_id = chn_info['app_id']
        params = {
            'bankCode': _get_bank_type(agent_pay.bank_code),
            'bankName': _get_bank_name(agent_pay.bank_code).encode('utf-8'),
            'unionCode': agent_pay.unionpay_no,
            'bankBranchName': agent_pay.bank_name.encode('utf-8'),
            'outTradeNo': agent_pay.order_no,
            'bankCardNo': agent_pay.card_no,
            'identityCard': agent_pay.identity_card,  # 持卡人身份证号 +
            'money': int(agent_pay.total_fee * 100),
            'payName': agent_pay.account_name.encode('utf-8'),
            'payPhone': agent_pay.phone_number,
            'payType': '0',
            'bankProvince': agent_pay.account_province.encode('utf-8'),
            'bankCity': agent_pay.account_city.encode('utf-8'),
            'notifyUrl': '{}/pay/api/{}/xunlianpay/'.format(settings.NOTIFY_PREFIX, settings.AGENT_PAY_NOTIFY_PATH),
            'partnerId': _get_partner_id(app_id),
            'body': 'agent_pay',
        }
        params['sign'] = generate_sign(params, _get_draw_key(app_id))
        _LOGGER.info('xunlianpay draw data: %s', json.dumps(params))
        headers = {'Content-Type': 'application/json;charset=utf-8'}
        response = requests.post(_get_draw_gateway(app_id), json=params, timeout=5, headers=headers, verify=False)
        _LOGGER.info("xunlianpay draw rsp data: %s %s", response.status_code, response.text)
        data = json.loads(response.text)
        if data['resultCode'] == '10000':
            return params['outTradeNo'], '', response.text
        else:
            return params['outTradeNo'], '', '代付失败->' + str(response.text)

    def query_agent_pay(self, order_no):
        """
        查询代付请求
        :return:
        """
        agent_pay = get_agent_pay_order(order_no)
        chn = channel_admin_db.get_channel(int(agent_pay.channel_id))
        chn_info = json.loads(chn.info)
        app_id = chn_info['app_id']
        params = {
            'partnerId': _get_partner_id(app_id),
            'outTradeNo': agent_pay.order_no,
        }
        params['sign'] = generate_sign(params, _get_draw_key(app_id))
        _LOGGER.info('xunlianpay draw query data: %s', json.dumps(params))
        response = requests.post(_get_draw_query_gateway(app_id), json=params, timeout=3, allow_redirects=False)
        _LOGGER.info("xunlianpay draw query rsp data: %s %s", response.status_code, response.text)
        if response.status_code != 200:
            raise ParamError('xunlianpay query_agent_pay error')
        data = json.loads(response.text)
        if data['status'] == '1':
            results = '代付处理中->' + str(response.text)
        elif data['status'] == '2':
            results = '代付失败->' + str(response.text)
        elif data['status'] == '3':
            results = '代付成功->' + str(response.text)
        else:
            results = '未知状态->' + str(response.text)
        return results

    def query_balance(self, info):
        info = ast.literal_eval(info.encode("utf-8"))
        app_id = info['app_id']
        params = {
            'partnerId': _get_partner_id(app_id),
        }
        params['sign'] = generate_sign(params, _get_draw_key(app_id))
        _LOGGER.info('xunlianpay balance query data: %s', json.dumps(params))
        response = requests.post(_get_balance_gateway(app_id), json=params, timeout=3, allow_redirects=False)
        _LOGGER.info("xunlianpay balance query rsp data: %s %s", response.status_code, response.text)
        if response.status_code != 200:
            raise ParamError('xunlianpay query_agent_pay error')
        data = json.loads(response.text)
        return float(data['balance']) / 100.0


handler = XunlianPayHandler()
